/** 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package it.free.i.virtual.machine.hal.devicealias;

import it.free.i.AbstractFactory;
import it.free.i.management.JmxRegisterServer;
import it.free.i.naming.CachedNamingProviderProxy;
import it.free.i.naming.NamingBuilder;
import it.free.i.virtual.machine.BootstrapAble;
import it.free.i.virtual.machine.hal.LinkingOperationException;
import it.free.i.virtual.machine.hal.devicealias.controller.DeviceAliasController;
import it.free.i.virtual.machine.hal.devicealias.controller.DeviceAliasPortController;
import it.free.i.virtual.machine.hal.devicealias.controller.DeviceAliasPortMessageListener;
import it.free.i.virtual.machine.hal.devicealias.laboratory.DeviceAliasPortReadClockDaemon;
import it.free.i.virtual.machine.hal.devicealias.port.DeviceAliasPort;

import java.util.Enumeration;
import java.util.List;
import java.util.Properties;

import javax.naming.NamingException;

import org.apache.log4j.Logger;

/**  
 * This class is really a standard one. This is only a delegation pattern
 * @author stefano
 */
public class DeviceAliasBootStrapper implements BootstrapAble {
	
	private static Logger logger = null;
	
	static{
		logger = Logger.getLogger(DeviceAliasBootStrapper.class);
	}
	
	/**
	 * Made private by the singleton pattern
	 */
	public DeviceAliasBootStrapper() {
	}

	
	/**
	 * Activate the devices looking up the jndi properties needed
	 * @throws LinkingOperationException
	 */
	@SuppressWarnings("unchecked")
	public final void boot() throws LinkingOperationException {
		
		String scanPath = NamingBuilder.buildLookupName(DeviceAliasFactory.DEVICES_TREE_JNDI_NAME, DeviceAliasFactory.CONTEXT);
		Properties devicesConfiguration = null;
		
		try{
			logger.info("Device alias tree scan path: [" + scanPath + "]");
			devicesConfiguration = CachedNamingProviderProxy.getInstance().lookUpConfiguration(scanPath);
			
			Enumeration deviceNames = devicesConfiguration.keys();
			logger.info("Devices tree fetched, found " + devicesConfiguration.size() + ", creating:");
			
			String tmpName = null;
			String jndiDeviceName = null;
			String fullQualifiedJndiDeviceConfigPath = null;
			//Properties jndiDeviceConfig = null;
			DeviceAlias deviceAlias = null;
			
			while(deviceNames.hasMoreElements()){
				  tmpName = (String)deviceNames.nextElement();
				  jndiDeviceName = devicesConfiguration.getProperty(tmpName);
				  
				  fullQualifiedJndiDeviceConfigPath = NamingBuilder.buildLookupName(jndiDeviceName, 
						  															DeviceAliasFactory.DEVICES_TREE_JNDI_NAME, 
						  															DeviceAliasFactory.CONTEXT);
				  
				  logger.info("Loading jndi device definition for name: [" + fullQualifiedJndiDeviceConfigPath + "]");
				  
				  try{
					  //New virtual device creation
					  logger.info("Creating new virtual device [" + fullQualifiedJndiDeviceConfigPath + "]");
					  deviceAlias = (DeviceAlias)AbstractFactory.createFactory(DeviceAliasFactory.class).createObject(fullQualifiedJndiDeviceConfigPath); 
					  
					  //Register with the controller
					  logger.info("Registering virtualized device [" + deviceAlias.getName() + "] to device alias commander");
					  //DeviceAliasController.getInstance().addObserver(deviceAlias);
					  DeviceAliasController.getInstance().addNotificationListener(deviceAlias, null, null);
					  
					  //Activate device alias ports and link to the commander and to the feedback listener
					  //DeviceAliasPortController.getInstance().addObserver(deviceAlias);
					  
					  //Putting it in the inventory
					  logger.info("Adding virtualized device [" + deviceAlias.getName() + "] to device alias inventory");
					  DeviceAliasInventory.getInstance().put(deviceAlias.getName(), deviceAlias);
                      
					  //Handling device alias ports
					  logger.info("Linking device alias port for device alias: [" + deviceAlias.getName() + "]:");
					  //Activate device alias ports and link to the commander and to the feedback listener
					  List<DeviceAliasPort> deviceAliasPortList = deviceAlias.getPorts();
					  //ListIterator<DeviceAliasPort> deviceAliasPortListIterator = deviceAliasPortList.listIterator();
					  if( (deviceAliasPortList == null) || 
						  (deviceAliasPortList.size() == 0) ){
						 throw (new LinkingOperationException("No port device found for device " + deviceAlias.getName()));
					  }
					  
					  //Registering device alias instance in the jmx registry
					  JmxRegisterServer.getInstance().registerBean(deviceAlias);
					  logger.info("Device alias port found: " + deviceAliasPortList.size());
					  
					  for(DeviceAliasPort tmpDeviceAliasPort: deviceAliasPortList){
					      //Controller pattern: device alias port controller -> device alias port observer
						  DeviceAliasPortController.getInstance().addNotificationListener(tmpDeviceAliasPort, null, null);
						  logger.info(tmpDeviceAliasPort.getName() + " [device alias port controller -> device alias port]");
						  //Sub classes of the DeviceAliasPortSupportImpl are available for the client coder
						  //device alias port -> device alias port message observer  
						  tmpDeviceAliasPort.addNotificationListener(DeviceAliasPortMessageListener.getInstance(), null, null);
						  logger.info(tmpDeviceAliasPort.getName() + " [device alias port -> device alias port message listener]");
					      JmxRegisterServer.getInstance().registerBean(tmpDeviceAliasPort);
					  }
					  
				  }catch(Exception exc){
					  	 throw (new LinkingOperationException(exc));
				  }
				  
				  //Register for event runnable collector
				  DeviceAliasPortMessageListener.getInstance().setObserver(DeviceAliasPortReadClockDaemon.getInstance());
				  
				  //Run the reading clock
				  Runnable readinClock = DeviceAliasPortReadClockDaemon.getInstance();
				  Thread readinClockThread = new Thread(readinClock);
				  readinClockThread.start();
			}
			
		}catch(NamingException nExc){
			   throw (new LinkingOperationException(nExc));
		}
		
	}
}
